{*
  Object Pascal simple configuration manipulation routines
  @Author  Maciej Zagozda (2007-2008)
  @Licence Microsoft Reciprocal License (Ms-RL)
  @Website http://www.codeplex.com/intelitechdelphirtns/
}


unit ConfigRoutines;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, StdCtrls;

type
  TCSVColumns = array of TStringList;

  // csv functionality enclosed in an object

  TCSVObject = class(TObject)
  private
    fFileName: AnsiString;
    procedure SetFileName( const strFileName: AnsiString );
  public
    CSVColumns: TCSVColumns;
    procedure OpenFile; overload;
    procedure OpenFile( strFileName: AnsiString ); overload;
    function ToComboBox( cboDestination: TComboBox ): Boolean;
    function ToListView( lsvDestination: TListView ): Boolean;
  published
    property FileName: AnsiString read fFileName write SetFileName;

  end;

// fetches text file into open array of string lists
function ReadConfigTextFile( strFileName: ShortString ): TCSVColumns;
// closes text file opened by the previous function
function CloseConfigTextFile( stlResult: TCSVColumns ): Boolean;
// populates a combo box with CSV values from a text file
function ReadConfigComboBox( cboDestination: TComboBox; strFileName: ShortString; blnIgnoreHeader: Boolean ): Boolean;
// populates a list view with CSV values from a text file
function ReadConfigListView( lsvDestination: TListView; strFileName: ShortString; blnIgnoreHeader: Boolean ): Boolean;

implementation

function ReadConfigTextFile( strFileName: ShortString ): TCSVColumns;
  {
  function AddColumn( csvColumns: TCSVColumns; intColumn: Integer ): TCSVColumns;
  begin
    if Length( csvColumns ) <= intColumn then
    begin
      SetLength( csvColumns, intColumn +1 );
      csvColumns[intColumn] := TStringList.Create;
    end;
  end;
  }
var
  stlResult: TCSVColumns;
  tflSource: TextFile;
  strSource: AnsiString;
  intColumn: Integer;
  strColumn: AnsiString;
  intComma: Integer;
begin

  System.AssignFile(tflSource, strFileName);
  System.Reset(tflSource);
  try
    while not System.EoF(tflSource) do
    begin
      System.ReadLn(tflSource, strSource);

      intColumn := 0;

      if AnsiPos( ',', strSource ) >0 then
      begin

        repeat

          //ShowMessage( Format( 'Column=%d, String=%s', [intColumn, strSource] ));

          //stlResult := AddColumn( stlResult, intColumn );
          if Length( stlResult ) <= intColumn then
          begin
            SetLength( stlResult, intColumn +1 );
            stlResult[intColumn] := TStringList.Create;
          end;

          intComma := AnsiPos( ',', strSource);
          if ( intComma >0 ) then
          begin
            strColumn := Copy( strSource, 0, intComma -1 );
            strSource := Copy( strSource, intComma +1, 4096 );
            stlResult[intColumn].Add(strColumn);

            //ShowMessage( Format( 'More further columns, adding: %s to: %d', [strColumn, intColumn] ));

          end;

          intColumn := intColumn +1;

        until (AnsiPos(',', strSource) =0);

        if Length( stlResult ) <= intColumn then
        begin
          SetLength( stlResult, intColumn +1 );
          stlResult[intColumn] := TStringList.Create;
        end;
      end
      else
      begin
        // single column csv
       SetLength( stlResult, intColumn +1 );
       if not Assigned( stlResult[intColumn] ) then
       begin
         stlResult[intColumn] := TStringList.Create;
       end;
      end;

      //ShowMessage( Format( 'No further columns, adding: %s to: %d', [strSource, intColumn] ));
      stlResult[intColumn].Add( strSource );

    end;//while
  finally
    System.CloseFile(tflSource);
  end;

  Result := stlResult;

end;

function CloseConfigTextFile( stlResult: TCSVColumns ): Boolean;
var
  intA: Integer;
begin
  for intA := 0 to Length( stlResult )-1 do
  begin
    stlResult[intA].Free;
  end;
end;

function ReadConfigComboBox( cboDestination: TComboBox; strFileName: ShortString; blnIgnoreHeader: Boolean ): Boolean;
var
  stlResult: TCSVColumns;
  intgA, intgB: Integer;
begin
  stlResult := ReadConfigTextFile( strFileName );

  try
    cboDestination.Items.Clear;
    
    if blnIgnoreHeader then
    begin
      intgB := 1;
    end
    else
    begin
      intgB := 0;
    end;//if

    for intgA := intgB to Pred( stlResult[0].Count ) do
    begin
      cboDestination.Items.Add(stlResult[0][intgA])
    end;//for

    cboDestination.ItemIndex := -1;

  finally
    CloseConfigTextFile( stlResult );
  end;

  Result := True;

end;


function ReadConfigListView( lsvDestination: TListView; strFileName: ShortString; blnIgnoreHeader: Boolean ): Boolean;
var
  stlResult: TCSVColumns;
  intgA, intgB: Integer;
  intgC: Byte;
begin
  //SetLength( stlResult, lsvDestination.Columns.Count +1 );
  try
    stlResult := ReadConfigTextFile( strFileName );

    try
      lsvDestination.Items.BeginUpdate;
      lsvDestination.Items.Clear;

      if blnIgnoreHeader then intgC := 1 else intgC := 0;

      try
        for intgA := intgC to stlResult[0].Count - 1 do
        begin
          with lsvDestination.Items.Add do
          begin
            Caption := stlResult[0][intgA];
            for intgB := 1 to Length( stlResult ) -1 do
            begin
              SubItems.Add( stlResult[intgB][intgA] );
            end;//for
          end;//with
        end;//for
      except
        on Error: Exception do
        begin
          lsvDestination.Items.EndUpdate;
          Assert( True, (Format( 'ReadConfigListView: There was an error while processing the file %s (%s)', [strFileName, Error.Message] )));
        end;//on
      end;//try
    finally
      lsvDestination.Items.EndUpdate;
    end;
  finally
    CloseConfigTextFile( stlResult );
  end;


end;

procedure TCSVObject.SetFileName( const strFileName: AnsiString );
begin
  fFileName := strFileName;
end;

procedure TCSVObject.OpenFile;
begin
  Self.OpenFile(fFileName);
end;

procedure TCSVObject.OpenFile( strFileName: AnsiString );
begin
  Self.CSVColumns := ReadConfigTextFile( strFileName );

end;

function TCSVObject.ToComboBox(cboDestination: TComboBox): Boolean;
begin
  Result := ReadConfigComboBox( cboDestination, Self.fFileName, True );
end;

function TCSVObject.ToListView(lsvDestination: TListView): Boolean;
begin
  Result := ReadConfigListView( lsvDestination, Self.fFileName, True );

end;

end.
